#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/ipc.h>

#include <sys/shm.h>

#include <sys/sem.h>

#include <sys/types.h>

#include <sys/wait.h>

#include <time.h>



#define NUM_SUPPLIERS 2   

#define NUM_SMOKERS 3    



typedef struct {

    int material1;

    int material2;

} SharedData;



union semun {

    int val;

    struct semid_ds *buf;

    unsigned short *array;

};

int shmid;              

SharedData *shared_data;

int sem_mutex;          

int sem_smoker[3];        

int sem_supplier_mutex;   



int init_sem(int key, int init_val) {

    int semid = semget(key, 1, IPC_CREAT | 0666);

    union semun arg;

    arg.val = init_val;

    semctl(semid, 0, SETVAL, arg);

    return semid;

}



void P(int semid) {

    struct sembuf op = {0, -1, SEM_UNDO};

    semop(semid, &op, 1);

}



void V(int semid) {

    struct sembuf op = {0, 1, SEM_UNDO};

    semop(semid, &op, 1);

}



void supplier(int id) {

    srand(time(NULL) + id);

    while (1) {

        P(sem_supplier_mutex);  // 获取供应者互斥锁



        P(sem_mutex);  // 获取共享内存互斥锁



        // 随机生成两种材料组合（0:烟草, 1:纸, 2:胶水）

        shared_data->material1 = rand() % 3;

        shared_data->material2 = (shared_data->material1 + 1) % 3;

        

        printf("Supplier%d provides: %d and %d\n", id, 

               shared_data->material1, shared_data->material2);

        int missing = 3 - shared_data->material1 - shared_data->material2;

        V(sem_smoker[missing]); 

        

        V(sem_mutex);  

        

        sleep(1);  

        V(sem_supplier_mutex);  

    }

}



void smoker(int id) {

    while (1) {

        P(sem_smoker[id]);  // 等待材料

        

        // 检查是否匹配所需材料

        int needed = 3 - id;  // 每个抽烟者缺少的材料

        if ((shared_data->material1 + shared_data->material2) == needed) {

            printf("Smoker%d (needs %d) is smoking...\n", id, needed);

            sleep(1);

            V(sem_mutex);  // 通知供应者继续

        }

    }

}



int main() {



    key_t key = ftok(".", 's');

    shmid = shmget(key, sizeof(SharedData), IPC_CREAT | 0666);

    shared_data = (SharedData*)shmat(shmid, NULL, 0);



    sem_mutex = init_sem(0x1234, 1);  

    sem_supplier_mutex = init_sem(0x8765, 1);

    for (int i = 0; i < 3; i++) {

        sem_smoker[i] = init_sem(0x5678 + i, 0); 

    }



    for (int i = 0; i < NUM_SUPPLIERS; i++) {

        if (fork() == 0) {

            supplier(i);

            exit(0);

        }

    }



    for (int i = 0; i < NUM_SMOKERS; i++) {

        if (fork() == 0) {

            smoker(i);

            exit(0);

        }

    }



    for (int i = 0; i < NUM_SUPPLIERS + NUM_SMOKERS; i++) {

        wait(NULL);

    }



    shmdt(shared_data);

    shmctl(shmid, IPC_RMID, NULL);

    semctl(sem_mutex, 0, IPC_RMID);

    semctl(sem_supplier_mutex, 0, IPC_RMID);

    for (int i = 0; i < 3; i++) {

        semctl(sem_smoker[i], 0, IPC_RMID);

    }



    return 0;

}
